/*
 *  Copyright (C) 2007 Veit Wehner.
 *  All Rights Reserved.
 *
 *  This is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */



/** \file sw_update.c
 * \brief The logic of the update process resides here.           
 * \author Veit Wehner <veit.wehner@gmail.com>
 */

#include "sw_update.h"

OS_DECLARE_EXTERN_RESOURCE(FLASH_RESOURCE);

// import the Event from the TCP/IP Library
#if RECEIVE_PROTOCOLL == ETHERNET
OS_DECLARE_EXTERN_EVENT(evHttpNotify);
#endif

// A variable against Linker Optimization
volatile int x;


#if RECEIVE_PROTOCOLL == ETHERNET
// This Event is set by swupdate task. 
// It is not triggered by an hardware interrupt 
// handler or something
OS_DECLARE_EVENT(sync_event);

void eth_read_word(){
  waitEvent(&sync_event);
}

void eth_read(){
  waitEvent(&sync_event);
}
#endif

/**
 * This function will not be stripped by Linker optimization 
 * cause its function pointer is referenced in code. 
 */
void do_nothing(){
}

void init_sw_update(){
  x = 1;
}
/**
 * This function reads the segment data of one memory segment
 * @param swupdate instance contains segment properties amongst others
 */

void read_segment_data(struct swupdate *this){
  int len = this->elfe->lengths[this->elfe->segment_counter];
  SHOW(reading segment data);
  int i;
  int chunksize; 
  // Configuration of update security
  // kind security influences the packet length
  // As TEA only works on blocks of 8 byte, the
  // Injector has to fill up the packet with zeros
  // to a multiple of eight. Then packetlength itself
  // ( in YaNETBuffer ) is not equal to the real data length.
  // Therefore chunksize is reduced by one and the last byte
  // hold the real length.
  // Therefore, if communication between node should be based on 
  // a packet length of 32 i.e., but packets coming from the computer 
  // should have length of 31, assuming there is no need for encryption 
  // when injector node and computer are connected directly via UART i.e.
  // 
  // If the injector uses the Davies-Meyer Algorithm
  // to generate a cryptographic checksum from TEA,
  // an eight byte signature is added as packet suffix.
  // Packet size of incoming traffic differs again from 
  // packet size of outgoing packets ( same reason as above ).
#if ENCRYPTION==TEA
  chunksize = CHUNKSIZE-1;
#elif HASH_CHECKSUM == DAVIES_MEYER
  chunksize = CHUNKSIZE - sizeof(unsigned long)*HASH_LENGTH;
#else 
  chunksize = CHUNKSIZE;
#endif
  // this is the number of packets expected by the node.
  // Value is obtained from packet size and segment length
  int no_of_chunks  = (!(len%(chunksize))) ? (len/(chunksize)) : (len/(chunksize) + 1);
  SHOW_DEC(no_of_chunks);
  for (i=0;i<no_of_chunks;i++){
#if DEBUG==2
    SHOW(CHUNK OF DATA);
#endif
    
    // The Injector Node has to wait for the partner node to be ready. 
    // If he receives the ready signal, he can request the next packet 
    // from the computer and forward it to the partner. 
    //
    // Else, if the node is destination of an update command, and the 
    // partner node is the Injector, he does not have to wait. 
    // In this case, the wait function has been linked against 
    // the do_nothing dummy function and the call has no real semantic meaning. 
    // Its just necessary to signal the own readyness in one of the following steps.
    this->wait_for_sync(this);
    
    
    // Checks for the integrity of packet data 
    if (this->communice->acknowledgment == VERIFIED){
      if ( (i==(no_of_chunks -1) ) && ( len%chunksize !=0 )) {
        this->datalen = len - ((len/(chunksize) * chunksize));      
      }
      else { 
        this->datalen = chunksize;      
      }
      this->send_sync(this);
#if RECEIVE_PROTOCOLL != ETHERNET
      this->read(this);  
#else 
      eth_read();
#endif
    }
    // That implements the error treatment if a packet content 
    // does not correspond to the checksum.
    // Then, the packet will be requested/transmitted, 
    // again, unless the number of configured retries is reached.
    // Pay attention on how the request/forward step is repeated:
    // The semantic of the loop iteration has to be repeated, unless
    // it is the first iteration. Then, the corrupted packet was sent 
    // not within the loop and contained a short word length packet 
    // ( according to the update protocoll: containing the length of the segment )
    else {
      i--;
      if (i==0) {
	this->write_word(this);
      }
    }
    this->write(this);  
  }
  
}

void insert_recovery_segment_data(struct swupdate *swupd){
  unsigned short len = (unsigned short)end_label_program_flash - (unsigned short)(program_flash);
  swupd->elfe->elf_size = len;
  swupd->elfe->lengths[0] = len;
  while(!getResourceFor(&FLASH_RESOURCE, 20000));
  if (append(swupd->file_for_segment_data, (unsigned char*)program_flash, len)!=len){ printf("error\n"); }
  sync();
  releaseResource(&FLASH_RESOURCE);
}


void get_image(struct swupdate *this, int state){
  /** This function retreives the 
   *  a memory image. The src of the image can 
   *  be the computer, another node or a file 
   *  within the own external flash. 
   *  The protocoll specifying the order of 
   *  retreiving the image contents stays 
   *  unchanged in everycase.
   *  @param The SWUPDATE instance, wich holds 
   *  a pointer to an elf instance. 
   *  The values of the latter will be set 
   *  while retreiving the image.
   */
  this->elfe->segment_counter = 0;
  this->send_sync(this);  
#if RECEIVE_PROTOCOLL != ETHERNET
  this->read_word(this);  
#else 
  eth_read_word();
#endif
  
  this->elfe->nr = this->priv_word;
  SHOW_DEC(this->elfe->nr);
  this->elfe->nr++;
  this->write_word(this);
  // dont transmit the recovery segment when cloning
  int start_index  = 1; //(state == CLONE) ? 0 : 1;
  
  SHOW_DEC(state);
  for (this->elfe->segment_counter=start_index; this->elfe->segment_counter<this->elfe->nr; this->elfe->segment_counter++){
    this->wait_for_sync(this);
    this->send_sync(this);
#if RECEIVE_PROTOCOLL != ETHERNET
    this->read_word(this);  
#else 
    eth_read_word();
#endif
    SHOW(segment start address: );
    SHOW_HEX(this->priv_word);
    this->elfe->start_addresses[this->elfe->segment_counter] = this->priv_word;
    this->write_word(this);
    this->wait_for_sync(this);
    this->send_sync(this);
#if RECEIVE_PROTOCOLL != ETHERNET
    this->read_word(this);  
#else 
    eth_read_word();
#endif
    SHOW(segment length: );
    SHOW_HEX(this->priv_word);
    this->elfe->lengths[this->elfe->segment_counter] = this->priv_word;
    this->write_word(this);
    this->elfe->elf_size += this->elfe->lengths[this->elfe->segment_counter];
    read_segment_data(this);
  }
  SHOW(done image data reading);
}

void get_file_nos_to_store_image_in(struct swupdate *this){
  this->wait_for_sync(this);
  this->send_sync(this);
#if RECEIVE_PROTOCOLL != ETHERNET
  this->read_word(this);  
#else 
  eth_read_word();
#endif
  this->file_for_image_header = this->priv_word;
  this->write_word(this);
  this->wait_for_sync(this);
  this->send_sync(this);
#if RECEIVE_PROTOCOLL != ETHERNET
  this->read_word(this);  
#else 
  eth_read_word();
#endif
  this->write_word(this);
  this->file_for_segment_data = this->priv_word;
  SHOW_DEC(this->file_for_image_header);
  SHOW_DEC(this->file_for_segment_data);
}




/** This function receives an update command
*   @param *this Pointer to the swupdate instance 
*          that will store the command.
*/

void get_update_command(struct swupdate *this){
#if RECEIVE_PROTOCOLL == ETHERNET
  this->send_sync(this);
  eth_read_word();
#else
  this->read_word(this);  
#endif 
  this->update_command = this->priv_word; 
  SHOW_DEC(this->update_command);
}



/** This read the node id from infomem and sets 
*   the value in the swupdate instace. 
*   @param *this Pointer to the swupdate instance 
*          that will store the node id.
*/

void set_own_node_id(struct swupdate *this){
  unsigned short *p = (unsigned short*) 0x1002;
  this->own_node_id = *p; 
  SHOW_HEX_WORD(this->own_node_id);
}



/** This function receives the update node ids
*   @param *this Pointer to the swupdate instance 
*          that will store the update node ids.
*/

void get_update_node_ids(struct swupdate *this){
  char i;
  for (i=0; i<2; i++){
    this->send_sync(this);
    SHOW_HEX_WORD(this->communice->communication_partner);
    
#if RECEIVE_PROTOCOLL != ETHERNET
    this->read_word(this);  
#else 
    eth_read_word();
#endif
    if (i==0){
      this->src_node_id = this->priv_word; 
      SHOW_HEX(this->src_node_id);
    }
    else{
      this->dst_node_id = this->priv_word; 
      SHOW_HEX(this->dst_node_id);
    }
  }
}

// extern variables for Ethernet communication
#if RECEIVE_PROTOCOLL == ETHERNET
OS_DECLARE_EVENT(evHttpNotify);
unsigned char data[200];
unsigned char tx_data[5];
TranceiveVector_t  tv = {    
  .receive = {data, 0, 200},    
  .transmit = {tx_data, 5},
  .notify  = &evHttpNotify};
Connection_t *con;
#endif



/** This function transmits the update command
*   and the update node ids to the destination node. 
*   @param *this Pointer to the swupdate instance 
*          that holds these command parameters.
*/

void write_command_part(struct swupdate *this){
  this->priv_word = this->update_command;
  this->write_word(this);
  this->wait_for_sync(this);
  this->priv_word = this->src_node_id;
  this->write_word(this);
  this->wait_for_sync(this);
  this->priv_word = this->dst_node_id;
  this->write_word(this);
}



#if CLONING_ENABLED
/** This function is called at the begin of a cloning 
*   process by the src node. Here, the functions of the 
*   upper level are linked for cloning mode.
*   @param *sw_upd Pointer to the swupdate instance 
*          that holds the function pointer variables.
*/

int prepare_cloning(struct swupdate *sw_upd){
  SHOW(Somebody wants my genotype);
  if (sw_upd->own_node_id == (unsigned short)INJECTOR_NODE_ID){
    return INIT;
  }
  sw_upd->read_word = image_header_read_word;
  sw_upd->read = program_memory_read;
  sw_upd->communice->communication_partner = sw_upd->dst_node_id;
  sw_upd->elfe->wich_word = 0;
  sw_upd->elfe->inner_segment_counter = 0;
  sw_upd->write_word = yanet_write_word;
  sw_upd->write = yanet_write;
  sw_upd->send_sync = do_nothing;
  
  sw_upd->wait_for_sync = wait_for_remote_node;
  write_command_part(sw_upd);
  
  SPIDevice_t_ AT45 = {
    .pin_cs = { .base= P5IN_, .mask = (1<<4), },
  };
  unsigned char command[8];
  int genotype_header = FILE_FOR_CLONING;
  get_elf_info(&AT45, command, sw_upd, genotype_header, 0);
  sw_upd->elfe->nr--;
  sleep(10000000);
  return CLONE;
  
}
#endif



/** This function has the logic that a node can 
*   determine its role within the update process. 
*   By evaluating the type of command and comparing 
*   his own node id with the dest and src ids of the 
*   command he is able to set up the communication, maybe 
*   the external flash device etc in order to prepare the 
*   update process.
*   @param *this Pointer to the swupdate instance.
*          
*/

int process_update_command(struct swupdate *this){
  if (this->dst_node_id == this->own_node_id){
    if ((this->src_node_id == 0) || (this->src_node_id == this->dst_node_id)){
      this->communice->communication_partner = (unsigned short)INJECTOR_NODE_ID;
    }
    else{
      this->communice->communication_partner = this->src_node_id;
    }
  } 
  
  else {
    if ((this->src_node_id == 0) || (this->src_node_id == this->dst_node_id)){
      this->communice->communication_partner = this->dst_node_id;
    }
    else{this->communice->communication_partner = this->src_node_id;}
  }
  write_command_part(this);
  
  int state = 0;
  if ((this->src_node_id == 0) || (this->src_node_id == this->dst_node_id)){
    get_file_nos_to_store_image_in(this);
  }
  
  else if ((this->src_node_id == this->own_node_id)&&(this->own_node_id!=this->dst_node_id)){
    
    state = prepare_cloning(this);
  }
  
  if (this->dst_node_id == this->own_node_id)
    { 
      setup_external_flash(this);
      insert_recovery_segment_data(this);
    }
  if ((this->src_node_id != this->dst_node_id)|| (this->own_node_id != this->src_node_id)){    
    get_image(this, state);
  }
  return 0;
}



/** This function sorts the start addresses and the lengths 
*   of the elf image header. 
*   This needed to probe for a good place 
*   for the special recovery segment.

*   param *elfe Pointer to the elf instance.
*/ 

void bubble_sort(struct elf *elfe){
  char swapped = 1;
  while(swapped)
    {
      swapped = 0;
      int j;
      for (j=1;j<elfe->nr;j++)
	{
	  unsigned int a1 = elfe->start_addresses[j];
	  unsigned int a2 = elfe->start_addresses[j+1];
	  unsigned int l1 = elfe->lengths[j];
	  unsigned int l2 = elfe->lengths[j+1];
	  if(a1 < a2)
	    {
	      elfe->start_addresses[j]= a2;
	      elfe->start_addresses[j+1] = a1;
	      elfe->lengths[j] = l2;
	      elfe->lengths[j+1] = l1;
	      swapped = 1;
	    }
	}
    }
} 



/** This function calculates a good place for the recovery segment 
*   that has to be inserted between the elf segments of the application 
*   that will be installed. 
*   The function is called with a local copy of the 
*   original header so that it does not modify this one 
*   (because it has to sort the segments).
*   @param elfe copy of the image header (call by copy)
*          
*/

unsigned int determine_my_start_address(struct elf elfe){
  int len = elfe.lengths[0];
  bubble_sort(&elfe);
  elfe.start_addresses[0] = (unsigned int)0xffe0 - len;
  int i;
  for (i=1; i<elfe.nr-1 ; i++){
    if (elfe.start_addresses[0] < (elfe.start_addresses[i] + elfe.lengths[i]))
      {
        elfe.start_addresses[0] = elfe.start_addresses[i] - (len + 50);
      }
    else {
      if (i == (elfe.nr -1))
      {
	return RECOVERY_SEGMENT_ERROR;
      }
    }
  }
  SHOW_HEX_WORD(elfe.start_addresses[0]);
  return elfe.start_addresses[0];
}  


// swupdate instance holding the 
// all information for the update process
struct swupdate sw_upd;

// instance for the communication
struct communication com;

// in case of ethernet connection, the task shall not 
// waste resouces until an update command goes in
char start_task = 0;

// low priority swupdate Task does not need to reserve the 
// ram that is used during reprogramming
OS_DECLARE_TASK(SWUPDATE,500,50); 


/** This function receives an update command
*   @param *this Pointer to the swupdate instance 
*          that will store the command.
*/
OS_TASKENTRY(SWUPDATE) {
  at45fs_init();
  while(1){
    sw_upd.communice = &com;
    // for the cryptographic checksum
    com.acknowledgment = VERIFIED;
    //defaults:
    sw_upd.communice->communication_partner = 0;
    sw_upd.file_for_image_header = DEFAULT_HEADER_FILE;
    sw_upd.file_for_segment_data = DEFAULT_SEGMENT_FILE;
    
    // Function binding process. Here the binding is done 
    // staticly by preprocessor defines. But the are also some
    // dynamic binding (i.e. in the prepare_cloning method).
#if RECEIVE_PROTOCOLL == YANET
    sw_upd.communice->communication_partner = (unsigned short)INJECTOR_NODE_ID;
#endif
    set_own_node_id(&sw_upd);
    
#if DEBUG || RECEIVE_PROTOCOLL==UART
    uart_device_t uart  = uart_getDevice(uart_console_device_number);
    sw_upd.communice->uart= uart;
#endif
    SHOW(Binding Functions..);
#if RECEIVE_PROTOCOLL==YANET
    sw_upd.read_word = yanet_read_word;
    sw_upd.read = yanet_read;
    sw_upd.write = at45_write;
    sw_upd.write_word = do_nothing;
    sw_upd.send_sync = cc1100_sync;
    sw_upd.wait_for_sync = do_nothing;
#elif RECEIVE_PROTOCOLL==UART
    sw_upd.read_word = uart_read_word;
    sw_upd.read = uart_read;
    sw_upd.send_sync = uart_sync_pc;
#if !TEST
    sw_upd.write_word = yanet_write_word;
    sw_upd.write = yanet_write;
    sw_upd.wait_for_sync = wait_for_remote_node;
#else
    sw_upd.write_word = do_nothing;
    sw_upd.write = do_nothing;
    sw_upd.wait_for_sync = do_nothing;
#endif
    
#elif RECEIVE_PROTOCOLL==ETHERNET
    sw_upd.read = enc28j60_read2;
#if !TEST
    sw_upd.wait_for_sync = wait_for_remote_node;
    sw_upd.write_word = yanet_write_word;
    sw_upd.write = yanet_write;
#else
    sw_upd.wait_for_sync = do_nothing;
    sw_upd.write_word = do_nothing;
    sw_upd.write = do_nothing;
#endif
    sw_upd.send_sync = enc28j60_sync_pc;
    init_ethernet(&sw_upd, con, &tv);
    con = sw_upd.communice->con;
    
#endif
    
#if (ENCRYPTION || HASH_CHECKSUM)
    get_key_or_initial_hash_from_infomem();
#endif
    struct elf e_i;
    sw_upd.elfe = &e_i;
    init_yanet();
#if RECEIVE_PROTOCOLL==ETHERNET      
    start_task = 1;
#endif
    
    get_update_command(&sw_upd);
    get_update_node_ids(&sw_upd);
    process_update_command(&sw_upd);
    
    if  (sw_upd.dst_node_id == sw_upd.own_node_id){
      if (sw_upd.src_node_id != sw_upd.dst_node_id){ 
        // get the fileblocks of the file where the image was put
	getFilePointers(sw_upd.file_for_segment_data, sw_upd.elfe->block_list);
	// calculate the number of these blocks
	sw_upd.elfe->no_of_used_blocks = (sw_upd.elfe->elf_size + (FLASH_PAGESIZE*FLASH_BLOCKSIZE - 1))/
          (FLASH_PAGESIZE * FLASH_BLOCKSIZE);
        // find a place for the recovery segment
        sw_upd.elfe->start_addresses[0] = determine_my_start_address(*sw_upd.elfe);	
      }
      // save the image header in a file that will be read during reprogramming
      // when RAM and flash are lost.
      save_elf_header(&sw_upd); 
      
      if (sw_upd.update_command != JUST_STORE_AN_IMAGE){
        // start the reprogramming. 
	program_flash();
      }   
    }
    
#if RECEIVE_PROTOCOLL==ETHERNET
    SHOW(Closed Connection);
    tcp_close(sw_upd.communice->con);  
#endif
    WDTCTL = 0;
  }
}


/** This task waits permanently for incoming data. 
*   On receiving it sets a sync Event in order to tell 
*   the other task that some data can be processed now. 
*/
#if RECEIVE_PROTOCOLL == ETHERNET
OS_DECLARE_TASK(Synchronizer,100,100);
OS_TASKENTRY(Synchronizer){
  while(1){
    while (start_task == 0){
      sleep(10000);
    }
    sw_upd.read(&sw_upd);
    setEvent(&sync_event);
  }
}
#endif


